package com.general.demo;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;


import java.util.Collections;

/**
 * RedisLock class
 *
 * @author junhui
 * @date 19-1-24
 */
@Component
public class RedisLockUtil {
    private static final Logger log = LoggerFactory.getLogger(RedisLockUtil.class);

    private static final String LOCK_SUCCESS = "OK";
    private static final String SET_IF_NOT_EXIST = "NX";
    private static final String SET_WITH_EXPIRE_TIME = "PX";
    private static final Long RELEASE_SUCCESS = 1L;

    @Autowired
    protected RedisTemplate redisTemplate;


    public boolean tryLock(String lockKey, String requestId, int expireTime) {
        log.info("尝试加锁lockKey:{},requestId:{}", lockKey, requestId);
        try {
            String result = (String) redisTemplate.execute(new RedisCallback<String>() {
                @Override
                public String doInRedis(RedisConnection connection) throws DataAccessException {
                    Jedis jedis = (Jedis) connection.getNativeConnection();
                    // spring 封装的 redisTemplate 没有这种操作,所以使用jedis
                    // 当key存在:不做任何操作;   存在:存入requestId且加上过期时间,防止服务挂了之后,key成了死锁(过期后可用)
                    return jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);
                }
            });
            log.info("result is " + result);
            if (LOCK_SUCCESS.equals(result)) {
                log.info("加锁成功");
                return true;
            } else {
                log.info("加锁失败,lockKey:{},requestId:{}", lockKey, requestId);
                return false;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    public boolean releaseLock(String lockKey, String requestId) {
        log.info("尝试解锁lockKey:{},requestId:{}", lockKey, requestId);
        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end";
        Object result = redisTemplate.execute(new RedisCallback<Object>() {
            @Override
            public Object doInRedis(RedisConnection connection) throws DataAccessException {
                Jedis jedis = (Jedis) connection.getNativeConnection();
                // 执行lua脚本进行解锁
                return jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId));
            }
        });
        log.info("解锁 result is {}", result.toString());
        if (RELEASE_SUCCESS.equals(result)) {
            log.info("解锁成功");
            return true;
        } else {
            log.info("解锁失败");
            return false;
        }
    }

}
